package me.gavin.util

import java.math.BigDecimal

private class PointB(var x: BigDecimal, var y: BigDecimal)

private operator fun StringBuilder.plus(str: String?): StringBuilder = append(str)

/*
 * @from https://www.jianshu.com/p/8cb39eb265e7
 */
object Absolute2Relative4SVG {

    fun path2Relative(path: String): String {
        val sb = StringBuilder()
        val end = PointB(0.toBigDecimal(), 0.toBigDecimal())
        val start = PointB(end.x, end.y)
        val pattern = "([MmLlHhVvQqTtCcSsAa][^MmLlHhVvQqTtCcSsAaZz]+|[Zz])".toPattern()
        val matcher = pattern.matcher(path)
        while (matcher.find()) {
            val part = matcher.group()
            val type = part[0]

            if (type in "Zz") {
                sb + "z"
                end.x = start.x
                end.y = start.y
                continue
            }

            val count = when (type) {
                in "HhVv" -> 1
                in "MmLlTt" -> 2
                in "QqSs" -> 4
                in "Cc" -> 6
                in "Aa" -> 7
                else -> error("Syntax Error")
            }

            part.drop(1)
                .split("[ ,]".toRegex())
                .mapNotNull { it.toBigDecimalOrNull() }
                .withIndex()
                .groupBy { it.index / count } // 切割 - 前后类型相同省略
                .map { it.value.let { it.map { it.value } } }
                .forEach { sb + transform(type, it, start, end) }
        }
        return sb.toString()
    }

    private fun transform(type: Char, ls: List<BigDecimal>, start: PointB, end: PointB): String {
        return when (type) {
            'm' -> {
                "$type${ls.joinToString(",")}".also {
                    end.x += ls[ls.lastIndex - 1]
                    end.y += ls.last()
                    start.x = end.x
                    start.y = end.y
                }
            }
            'h' -> {
                "$type${ls.joinToString(",")}".also {
                    end.x += ls.last()
                }
            }
            'v' -> {
                "$type${ls.joinToString(",")}".also {
                    end.y += ls.last()
                }
            }
            in "lqtcsa" -> {
                "$type${ls.joinToString(",")}".also {
                    end.x += ls[ls.lastIndex - 1]
                    end.y += ls.last()
                }
            }
            'M' -> {
                val ls2 = ls.mapIndexed { i, v -> v - if (i % 2 == 0) end.x else end.y }
                "${type.toLowerCase()}${ls2.joinToString(",")}".also {
                    end.x = ls[ls.lastIndex - 1]
                    end.y = ls.last()
                    start.x = end.x
                    start.y = end.y
                }
            }
            'H' -> {
                val ls2 = ls.map { it - end.x }
                "${type.toLowerCase()}${ls2.joinToString(",")}".also {
                    end.x = ls.last()
                }
            }
            'V' -> {
                val ls2 = ls.map { it - end.y }
                "${type.toLowerCase()}${ls2.joinToString(",")}".also {
                    end.y = ls.last()
                }
            }
            'A' -> {
                val ls2 = ls.mapIndexed { i, v -> v - if (i % 2 == 0) end.x else end.y }
                arrayOf(
                    *ls2.take(2).toTypedArray(),
                    *ls.drop(2).dropLast(2).toTypedArray(),
                    *ls2.takeLast(2).toTypedArray()
                ).let { "${type.toLowerCase()}${it.joinToString(",")}" }.also {
                    end.x = ls[ls.lastIndex - 1]
                    end.y = ls.last()
                }
            }
            in "LQTCS" -> {
                val ls2 = ls.mapIndexed { i, v -> v - if (i % 2 == 0) end.x else end.y }
                "${type.toLowerCase()}${ls2.joinToString(",")}".also {
                    end.x = ls[ls.lastIndex - 1]
                    end.y = ls.last()
                }
            }
            else -> error("Syntax Error")
        }
    }

}